package com.hhxyjsj.plugin;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

/**
 * 
 * 在打包应用 时自动给html、htm以及jsp文件中引用的js、css文件加上版本号
 *
 * @author hhxyj
 */
@Mojo(name = "addVersion",defaultPhase = LifecyclePhase.PACKAGE)
public class AddVersionPlugin extends AbstractMojo {

	/**
	 * 版本号,默认值为当前时间戳
	 */
	@Parameter
	private String version;
	
	/**
	 * 要增加版本号的资源文件,默认只给.html和.htm文件中引用的js、css加版本号
	 */
	@Parameter(defaultValue = "*.html,*.htm")
	private List<String> includs;
	
	/**
	 * 项目构建输出目录，默认为target/
	 */
	@Parameter(property = "project.build.directory")
	private String directory;
	
	/**
	 * 项目打包输出文件的名称，默认为${project.artifactId}${project.version}.
	 */
	@Parameter(property = "project.build.finalName")
	private String finalName;
	
	/**
	 * 包的类型
	 */
	@Parameter(property = "project.packaging")
	private String packaging;
	
	/**
	 * 配获资源文件的正则表达式
	 */
	private List<Pattern> pattList = new ArrayList<Pattern>();
	
	public void execute() throws MojoExecutionException, MojoFailureException {
		
		//判断项目最终是否是生成war包
		if(!"war".equalsIgnoreCase(packaging)){
			return;
		}
		
		//验证版本号参数	
		checkVersion();
		
		//生成匹配资源文件的正则表达式
		generatePatter();
		
		//给资源文件中引用的js和css文件添加版本号 
		addVersion();
		
		//生成war包
		fileToWar();
	}
	
	/**
	 * 检查版本号参数
	 */
	private void checkVersion(){
		//如果该参数没有传值参将该参数设置一个默认值，即当前时间戳
		if(version == null || "".equals(version)){
			version = String.valueOf(System.currentTimeMillis());
		}
	}
	
	/**
	 * 生成匹配资源文件的正则表达式
	 */
	private void generatePatter(){
		for(String str : includs){
			if(str.endsWith(".html") || str.endsWith(".htm") || str.endsWith(".jsp")){
				pattList.add(Pattern.compile(str.replace("*", "[a-zA-Z0-9/.:_\\-]*")));
			}
		}
	}
	
	/**
	 * 给资源文件中引用的js和css文件添加版本号 
	 * 
	 * @throws MojoFailureException
	 */
	private void addVersion() throws MojoFailureException{
		//资源文件的根目录
		File file = new File(directory + "/" + finalName);
		if(!file.exists() && file.isFile()){
			return;
		}
		
		addVersion(file);
	}
	
	/**
	 * 递归找到匹配的资源文件后，为其中引用的js和css文件 添加版本号
	 * @param file
	 * @throws MojoFailureException 
	 */
	private void addVersion(File file) throws MojoFailureException{
		if(file.isDirectory()){
			for(File f : file.listFiles()){
				addVersion(f);
			}
		}else{
			if(checkPattList(file.getPath())){
				setupVersion(file);
			}
		}
	}
	
	/**
	 * 检查资源文件是否匹配
	 * 
	 * @param path
	 * @return
	 */
	private boolean checkPattList(String path){
		for(Pattern patt : pattList){
			if(patt.matcher(path.replaceAll("\\\\", "/")).matches()){
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 更新文件中引用的静态资源的版本号
	 * 
	 * @param file
	 * @throws MojoFailureException
	 */
	private void setupVersion(File file) throws MojoFailureException{
		BufferedReader br = null;
		BufferedWriter bw = null;
		//文件 的完整路径，包括路径 和文件 名
		String filePath = file.getPath();
		try{
		    //先改名，使用一个临时文件 名称
			file.renameTo(new File("temp"));
			br = new BufferedReader(new InputStreamReader(new FileInputStream(new File("temp")), "utf-8"));
			bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filePath, false), "utf-8"));

			String line;
			while((line = br.readLine()) != null){
				if(line.contains(".js?")){
					line = line.replaceAll("\\.js\\?", ".js?v=" + version + "&");
				}else if(line.contains(".js")){
					line = line.replaceAll("\\.js", ".js?v=" + version);
				}else if(line.contains(".css?")){
					line = line.replaceAll("\\.css\\?", ".css?v=" + version + "&");
				}else if(line.contains(".css")){
					line = line.replaceAll("\\.css", ".css?v=" + version);
				}
				
				bw.write(line + "\n");
			}
		}catch(Exception e){
			throw new MojoFailureException(e.getMessage(), e);
		} finally{
			if(br != null){
				try {
					br.close();
				} catch (IOException e) {
					 throw new MojoFailureException(e.getMessage(), e);
				}
			}
			if(bw != null){
				try {
					bw.close();
				} catch (IOException e) {
					 throw new MojoFailureException(e.getMessage(), e);
				}
			}
		}
	}	
	
	/**
	 * 将所有的资源文件打包到一个war包里
	 * 
	 * @throws MojoFailureException 
	 */
	private void fileToWar() throws MojoFailureException{  
        File sourceFile = new File(this.directory + "/" + this.finalName);  
        File warFile = new File(this.directory + "/" + this.finalName +".war");  
        ZipOutputStream zos = null;  
        try {  
            zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(warFile,false)));  
            for(File file : sourceFile.listFiles()) {
            	fileToWar(file, zos, "");
            }
        } catch (IOException  e) {  
            throw new MojoFailureException(e.getMessage(), e);    
        } finally{  
            try {  
                if(null != zos) {
                	zos.flush();
                	zos.close();  
                }
            } catch (IOException e) {  
                throw new MojoFailureException(e.getMessage(), e);
            }  
        }  

    }  
	
	/**
	 * 递归将所有的资源文件及文件夹打包到一个war包里
	 * 
	 * @param sourceFile
	 * @param zos
	 * @param path
	 * @throws IOException
	 */
	private void fileToWar(File sourceFile,ZipOutputStream zos, String path) throws IOException{
    	if(sourceFile.isDirectory()){
    		String srcPath = path + sourceFile.getName() + "/";
            zos.putNextEntry(new ZipEntry(srcPath)); 
            for(File file : sourceFile.listFiles()){
            	fileToWar(file, zos, srcPath);
            }
    	}else{
            //创建war实体，并添加进压缩包  
            zos.putNextEntry(new ZipEntry(path + sourceFile.getName()));  
            //读取待压缩的文件并写进压缩包里  
            BufferedInputStream bis = null;  
            int read = 0; 
            byte[] bufs = new byte[1024];  
            try{
            	bis = new BufferedInputStream(new FileInputStream(sourceFile));  
		        while((read = bis.read(bufs, 0, bufs.length)) != -1){  
		            zos.write(bufs,0,read);  
		        }  
            } finally {
            	if(bis != null){
            		bis.close();
            	}
            }
    	}
	}
	
	public static void main(String[] args) {
		String version = "1.2";
		String line = "../../istatic/js/common/separateCom.js";
		if(line.contains(".js?")){
			line = line.replaceAll("\\.js\\?", ".js?v=" + version + "&");
		}else if(line.contains(".js")){
			line = line.replaceAll("\\.js", ".js?v=" + version);
		}else if(line.contains(".css?")){
			line = line.replaceAll("\\.css\\?", ".css?v=" + version + "&");
		}else if(line.contains(".css")){
			line = line.replaceAll("\\.css", ".css?v=" + version);
		}
		System.out.println(line);
	}
}
